package ms.jasim.pddl;

import ms.jasim.model.Actor;
import ms.jasim.model.Capability;
import ms.jasim.model.Decomposition;
import ms.jasim.model.Dependency;
import ms.jasim.model.Goal;
import ms.jasim.model.GoalRequest;
import ms.jasim.model.JasimModel;
import ms.jasim.model.items.PropertyDescriptor;

public class BrylPddlGenerator implements IPddlProblemGenerator {

	protected JasimModel model;
	protected PddlProblem problem;
	protected PropertyDescriptor numInstanceDescriptor;
	
	protected String[][] decomposePredicates = {
			{ PddlPredicate.AND_DECOMPOSE2, PddlPredicate.AND_DECOMPOSE3 },
			{ PddlPredicate.OR_DECOMPOSE2, PddlPredicate.AND_DECOMPOSE3 }
		};

	public BrylPddlGenerator() {
	}
	
	public PddlProblem getPddlProblem(JasimModel model) {
		if (this.problem == null || model != this.model) {
			this.model = model;
			this.problem = generateProblem();
		}
		return this.problem;
	}

	protected PddlProblem generateProblem() {
		PddlProblem pddl = new PddlProblem(model);
		pddl.setMetric(null);
		
		numInstanceDescriptor = model.findPropertyDescriptor(Actor.class, PddlPredicate.NUM_INSTANCE);
		
		pddl.objects.add(new PddlObject("foo", PddlObjectType.T_GTYPE));
		
		for (Goal g: model.getGoals())
			generateGoal(pddl, g);
		
		for (Actor a : model.getActors()) {
			int numIns = (Integer) a.resolveProperty(numInstanceDescriptor).getValue(null);
			if (numIns > 0) {
				PddlObject pddlActor = new PddlObject(a.getName(), PddlObjectType.T_ACTOR);
				pddl.objects.add(pddlActor);
				
				generateCapabilities(pddl, a, pddlActor);
				
				// introduce task based on actor's requests
				for (GoalRequest request : a.getRequests())
					if (request.getGoal() != null) {
						PddlObject pddlGoal = pddl.objects.get(request.getGoal().getName());
						pddl.facts.add(new PddlPredicate(PddlPredicate.REQUESTS, pddlActor, pddlGoal));
						pddl.goals.add(new PddlPredicate(PddlPredicate.SATISFIED, pddlGoal));
					}
			}
		}
		
		
		for (Actor a: model.getActors()) {
			PddlObject pddlActor = pddl.objects.get(a.getName());
			generateDependencies(pddl, a, pddlActor);	
		}
		
		return pddl;
	}

	protected void generateCapabilities(PddlProblem pddl, Actor a, PddlObject pddlActor) {
		for (Capability cap : a.getCapability()) {
			PddlObject pddlGoal = pddl.objects.get(cap.getGoal().getName());
			pddl.facts.add(new PddlPredicate(PddlPredicate.CAN_PROVIDE, pddlActor, pddlGoal));
		}
	}

	private void generateDependencies(PddlProblem pddl, Actor a, PddlObject pddlActor) {
		for (Dependency de: a.getDependency()) {
			for (Actor ta: de.getTargetActor()) {
				PddlObject pddlTargetActor = pddl.objects.get(ta.getName());
				for (Goal g: de.getGoal()) {
					PddlObject pddlGoal = pddl.objects.get(g.getName());
					pddl.facts.add(new PddlPredicate(PddlPredicate.CAN_DEPEND_ON_GOAL, pddlActor, pddlTargetActor, pddlGoal));
				}
				if (de.getGoal().size() == 0)
					pddl.facts.add(new PddlPredicate(PddlPredicate.CAN_DEPEND_ON, pddlActor, pddlTargetActor));
			}
			/*if (de.getTargetActor() !=  null) {
				PddlObject pddlTargetActor = pddl.objects.get(de.getTargetActor().getName());
				if (de.getGoal() != null) {
					PddlObject pddlGoal = pddl.objects.get(de.getGoal().getName());
					pddl.facts.add(new PddlPredicate(PddlPredicate.CAN_DEPEND_ON_GOAL, pddlActor, pddlTargetActor, pddlGoal));
				}
				else
					pddl.facts.add(new PddlPredicate(PddlPredicate.CAN_DEPEND_ON, pddlActor, pddlTargetActor));
			}*/
		}
	}
	
	protected void generateGoal(PddlProblem pddl, Goal g) {
		PddlObject pddlGoal = new PddlObject(g.getName(), PddlObjectType.T_GOAL);
		pddl.objects.add(pddlGoal);
		
		for (Decomposition de : g.getDecomposition()) {
			PddlPredicate pred = new PddlPredicate();
			
			int size = de.getSubGoals().size();
			
			if (size >= 2 && size <= 3)
				pred.setFunctor(decomposePredicates[de.getType().getValue()][size - 2]);
			
			pred.getArgs().add(pddlGoal);
			for (Goal sg: de.getSubGoals())
				pred.getArgs().add(sg.getName());
			pddl.facts.add(pred);
		}
	}

	@Override
	public void processPostPddlCommand(PddlProblem prop) {
		// TODO Auto-generated method stub
		
	}
}
